package org.linlinjava.litemall.admin.service;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import org.linlinjava.litemall.admin.beans.Constants;
import org.linlinjava.litemall.admin.beans.dto.PointsDto;
import org.linlinjava.litemall.admin.beans.dto.UserDto;
import org.linlinjava.litemall.admin.beans.enums.PromptEnum;
import org.linlinjava.litemall.admin.util.RandomUtils;
import org.linlinjava.litemall.admin.vo.IntegralLogVO;
import org.linlinjava.litemall.core.notify.AwsNotifyService;
import org.linlinjava.litemall.core.notify.NotifyService;
import org.linlinjava.litemall.core.util.ResponseUtil;
import org.linlinjava.litemall.core.util.bcrypt.BCryptPasswordEncoder;
import org.linlinjava.litemall.db.domain.*;
import org.linlinjava.litemall.db.service.*;
import org.linlinjava.litemall.db.util.PageTools;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;

import static org.linlinjava.litemall.admin.beans.enums.AdminResponseCode.*;
import static org.linlinjava.litemall.admin.util.AdminResponseCode.MOBILE_CODE_ONE;
import static org.linlinjava.litemall.db.beans.Constants.INTEGRAL_LOG_TYPE_MANAGEMENT_ADD;
import static org.linlinjava.litemall.db.beans.Constants.INTEGRAL_LOG_TYPE_MANAGEMENT_SUBSCTRACT;

@Service
public class AdminUserInfoService {
    @Autowired
    private LitemallUserService litemallUserService;
    @Autowired
    private LitemallAddressService litemallAddressService;
    @Autowired
    private LitemallOrderService litemallOrderService;
    @Autowired
    private LitemallIntegralLogService integralLogService;
    @Autowired
    private LitemallCouponService couponService;
    @Autowired
    private LitemallCouponUserService couponUserService;

    @Autowired
    private LumiereCouponUserService lumiereCouponUserService;
    @Autowired
    private NotifyService notifyService;

    @Autowired
    private LitemallRechargeConsumptionService litemallRechargeService;

    @Autowired
    private LitemallRechargeConsumptionService rechargeConsumptionService;

    @Autowired
    private LitemallShopService litemallShopService;

    @Autowired
    private LumiereCloverChargesLogService lumiereCloverChargesLogService;

    @Autowired
    private LitemallTokenService litemallTokenService;

    @Autowired
    private LitemallGiftCardUserService giftCardUserService;

    @Autowired
    private AwsNotifyService awsNotifyService;

    // 编程式事务
    @Autowired
    private DataSourceTransactionManager transactionManager; // 事务管理器

    @Autowired
    private TransactionDefinition transactionDefinition; // 定义事务属性


    @Value("${sys.datetime}")
    public String SysDateTime;

    public Object userInfo(Integer userId){
        Map<String, Object> map = CollUtil.newHashMap();
        LitemallUser user = litemallUserService.findById(userId);
        List<LitemallAddress> addressList = litemallAddressService.findByUserId(userId);

        long couponCount = lumiereCouponUserService.count(Wrappers.lambdaQuery(LumiereCouponUser.class)
                .eq(LumiereCouponUser::getUserId, userId)
                .isNull(LumiereCouponUser::getUsedTime)
                .gt(LumiereCouponUser::getEndTime,LocalDateTime.now()));

        map.put("user", user);
        map.put("addressList", addressList);
        map.put("orderActualPrice", litemallOrderService.totalOrderSumByUser(userId));
        map.put("orders", litemallOrderService.count(Wrappers.lambdaQuery(LitemallOrder.class).eq(LitemallOrder::getUserId,userId)));
        map.put("couponCount",couponCount);
        map.put("cardBalance",giftCardUserService.getCardBalanceTotal(userId));
        map.put("topup", rechargeConsumptionService.getUserTopup(userId, (byte) 1));
        map.put("expend", rechargeConsumptionService.getUserTopup(userId, (byte) 2));
        return ResponseUtil.ok(map);
    }


    public Object resetPwd(Integer userId, String newPwd) {
        if(StringUtils.isEmpty(newPwd)){
            newPwd = RandomUtils.generateRandomStr(6);
        }
        LitemallUser user = litemallUserService.findById(userId);
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        String encodedPassword = encoder.encode(newPwd);
        LitemallUser update = new LitemallUser();
        update.setId(user.getId());
        update.setPassword(encodedPassword);
        litemallUserService.updateById(update);
        return ResponseUtil.ok(newPwd);
    }



    public Object integral(Integer userId, Integer integral) {
        LitemallUser user = litemallUserService.findById(userId);
        LitemallUser update = new LitemallUser();
        update.setId(user.getId());
        update.setPoints(user.getPoints().add(new BigDecimal(integral)));
        litemallUserService.updateById(update);
        return ResponseUtil.ok();
    }

    /**
     * 更新
     * 修改用户信息
     * @param dto
     * @return
     */
    public Object update(UserDto dto) {
        TransactionStatus transactionStatus = null;
        LitemallUser update = new LitemallUser();
        BeanUtils.copyProperties(dto, update);
        //状态为"禁用"不删除用户
        //update.setDeleted(dto.getStatus() != 0); // 0 -> false, 1 -> true
        update.setDeletedBy(111111);
        try {
            // 1. 开启事务
            transactionStatus = transactionManager.getTransaction(transactionDefinition);
            // 2. 更新用户信息
            litemallUserService.updateById(update);
            // 3. 检查重复用户名
            long repeatCount = litemallUserService.list().stream()
                    .filter(litemallUser -> litemallUser.getUsername().equals(dto.getUsername()))
                    .count();
            if (repeatCount >= 2) {
                throw new RuntimeException(); // 触发异常以回滚事务
            }
            // 4. 事务提交
            transactionManager.commit(transactionStatus);
        } catch (Exception e) {
            // 事务回滚
            if (transactionStatus != null) {
                transactionManager.rollback(transactionStatus);
            }
            // 返回失败响应
            return ResponseUtil.fail(ADMIN_USER_NAME_REPEAT, "重复用户名称，请重新输入");
        }
        // 5. 针对状态为1的额外操作
        if (dto.getStatus() == 1) {
            litemallTokenService.removeToken(dto.getId());
        }
        // 6. 返回成功响应
        return ResponseUtil.ok();
    }

    public Object updatePoint(PointsDto dto){
        Integer userId = dto.getUserId();
        BigDecimal points = dto.getPoints();
        Integer type = dto.getType();
        String remark = dto.getRemark();
        LitemallUser user = litemallUserService.findById(userId);
        LocalDateTime updateTime = user.getUpdateTime();

        if(user == null){
            return ResponseUtil.fail(ADMIN_USER_NOT_EXIST,"用户不存在");
        }

        if(type == 2  ){
            if(user.getPoints().subtract(points).compareTo(BigDecimal.ZERO) >= 0){
                user.setPoints(user.getPoints().subtract(points));
            }else{
                return ResponseUtil.fail(ADMIN_USER_POINT_NOT_ENOUGH,"用户积分不足");
            }
        }else{
            user.setPoints(user.getPoints().add(points));
        }
        //litemallUserService.updateWithOptimisticLocker(user,updateTime);
        litemallUserService.updateById2(user);

        //更新用户积分
        LitemallIntegralLog l = new LitemallIntegralLog();
        l.setUserId(user.getId());
        l.setPoints(user.getPoints());
        l.setUseType(1);
        if(type == 2){
            l.setType(INTEGRAL_LOG_TYPE_MANAGEMENT_SUBSCTRACT);
            l.setChangePoints(points.multiply(new BigDecimal(-1)));
        }else{
            l.setType(INTEGRAL_LOG_TYPE_MANAGEMENT_ADD);
            l.setChangePoints(points);
        }

        integralLogService.add(l);

        return  ResponseUtil.ok();
    }

    public Object pointDetail(Integer userId,Integer page,Integer limit,String sort,String order,Byte type){
        IPage<IntegralLogVO> IntegralLogVoList =integralLogService.selectJoinListPage(new Page<>(page, limit),IntegralLogVO.class,new MPJLambdaWrapper<LitemallIntegralLog>()
                .selectAll(LitemallIntegralLog.class)
                .selectAs(LitemallShop::getId, IntegralLogVO::getShopId)
                .selectAs(LitemallShop::getName, IntegralLogVO::getShopName)
                .selectAs(LitemallOrder::getId, IntegralLogVO::getOrderId)
                .leftJoin(LitemallRechargeConsumption.class,LitemallRechargeConsumption::getId,LitemallIntegralLog::getConsumptionId)
                .leftJoin(LitemallOrder.class,LitemallOrder::getId,LitemallRechargeConsumption::getOrderId)
                .leftJoin(LitemallOrderGoods.class,LitemallOrderGoods::getOrderId,LitemallOrder::getId)
                .leftJoin(LitemallShop.class,LitemallShop::getId,LitemallOrder::getShopId)
                .eq(LitemallIntegralLog::getUserId,userId)
                .eq(type!=null,LitemallIntegralLog::getType,type)
                .orderByDesc(LitemallIntegralLog::getAddTime)
        );
        return ResponseUtil.okPage(IntegralLogVoList);
    }


    @Transactional
    public Object grantExtenCoupon(Integer userId,Integer selectCouponId,Integer num, String remark) {
        LitemallCoupon coupon = couponService.findById(selectCouponId);
        LitemallUser user = litemallUserService.findById(userId);

        if(coupon == null){
            return ResponseUtil.fail(ADMIN_COUPON_NOT_EXIST,"优惠券不存在");
        }

        Integer count = couponUserService.countCoupon(selectCouponId);

        if(coupon.getTotal() <= count){
            return ResponseUtil.fail(ADMIN_COUPON_GRANT_OVER,"优惠券已发放完毕");
        }

        for (Integer i = 0; i < num; i++) {
            LitemallCouponUser  couponUser = new LitemallCouponUser();
            couponUser.setUserId(userId);
            couponUser.setStatus((short)0);
            couponUser.setUserName(user.getUsername());
            couponUser.setStartTime(coupon.getStartTime());
            couponUser.setEndTime(coupon.getEndTime());
            couponUser.setAddTime(LocalDateTime.now());
            couponUser.setUpdateTime(LocalDateTime.now());
            couponUser.setCouponId(selectCouponId);
            couponUser.setDeleted(false);
            couponUserService.add(couponUser);
            count ++;

            if(count == coupon.getTotal()){
                break;
            }
        }
        if (user.getMobile() != null) {
            awsNotifyService.sendSms(MOBILE_CODE_ONE+user.getMobile(), remark, org.linlinjava.litemall.db.beans.Constants.AWS_MESSAGE_TYPE_TRANSACTIONAL);
//            notifyService.notifySms(1+user.getMobile(), remark);
        }

        if (user.getEmail() != null) {
            notifyService.notifyMail(null, remark, user.getEmail());
        }
        return ResponseUtil.ok();
    }

    public Object cloverDetail(Integer userId,Integer page,Integer limit) {
        IPage<LumiereCloverChargesLog> cloverChargesLogs = lumiereCloverChargesLogService.page(new Page<>(page,limit),Wrappers.lambdaQuery(LumiereCloverChargesLog.class)
                .eq(LumiereCloverChargesLog::getUserId, userId));
        return ResponseUtil.okPage(cloverChargesLogs);
    }
}
